package simulator;

import graph.Graph;

import java.util.ArrayList;
import java.util.Vector;

import logs.Logger;
import simulator.processes.Process;
import simulator.processes.Scheduler;
import simulator.processes.SchedulerListener;

/**
 * Trida reprezentujici firmu.
 * 
 * @author Petr Altman
 */
public class Company implements DecisionStrategyListener, SchedulerListener {

	public static final int		
								/**
								 * Cas urcijici dobu vykladani jedne palety.
								 */
								TIME_UNLOAD_ONE = 1800,	// 30 minut
								/**
								 * Maximalni kapacita nakladaku.
								 */
								PALLET_COUNT = 6;
	
	private Graph world;
	
	private Scheduler scheduler;
	
	/**
	 * Vrati instanci planovace firmy.
	 * @return Instance planovace firmy.
	 */
	public Scheduler getScheduler() {
		return scheduler;
	}

	private City city;
	
	/**
	 * Vrati instanci mesta, ve kterem firma sidli.
	 * @return Instance mesta, ve kterem firma sidli.
	 */
	public City getCity() {
		return city;
	}

	private DecisionStrategy dstrategy = null;
	
	/**
	 * Vrati instanci grafu, ve kterem firma pusobi.
	 * @return Graf, ve kterem firma pusobi.
	 */
	public Graph getWorld() {
		return world;
	}

	private CompanyListener listener = null;
	
	/**
	 * Vrati listener firmy.
	 * @return Listener firmy.
	 */
	public CompanyListener getListener() {
		return listener;
	}

	/**
	 * Pripradi listener firme.
	 * @param listener Objekt implementujici rozhrani <code>CompanyListener</code>.
	 */
	public void setListener(CompanyListener listener) {
		this.listener = listener;
	}

	private ArrayList<Truck> trucks = new ArrayList<Truck>();

	private int totalTruckCount = 0;
	
	/**
	 * Cstor.
	 * @param world Graf, ve kterem ma firma pusobit.
	 * @param city Mesto, ve kterem ma firma sidlit.
	 */
	public Company(Graph world, City city) {
		super();
		this.world = world;
		this.city = city;
		
		scheduler = new Scheduler(this, Simulator.ACCEPTANCE_TIME_START);
		dstrategy = new DecisionStrategy(this);
	}
	
	/**
	 * Nastavi stavove hodnoty firmy do vychozich hodnot.
	 */
	public void reset() {
		scheduler.reset();
		trucks.clear();
		
		totalTruckCount = 0;
	}
	
	/**
	 * Zada firme objednavku.
	 * @param order Nova objednavka.
	 * @return Stav zda objednavka byla zarazena do planovace.
	 */
	public boolean addOrder(Order order) {
		return scheduler.add(order);
	}

	/**
	 * Prida nakladak do firmy.
	 * @param truck Novy nakladak.
	 */
	public void addTruck(Truck truck) {
		// pridani nakladaku do firmy i do planovace
		trucks.add(truck);
		scheduler.add(truck);
		
		totalTruckCount++;
	}
	
	/**
	 * Zpracuje nasledujici udalost v planovaci.
	 */
	public void nextEvent() {
		// zpracovani nasledujici udalosti a overeni, zda
		// je scheduler prazdny, pokud ano, tak firma konci
		if(scheduler.processNextEvent() == null)
			// vyvolani udalosti o ukonceni
			if(listener != null)
				listener.onEnd();
	}
	
	/**
	 * Zpracuje objednavku a vrati stav zpracovani.
	 * @param order Objednavka ke zpracovani.
	 * @return Stav, zda byla objednavka zpracovana nebo odmitnuta.
	 */
	public boolean processOrder(Order order) {
		boolean result = dstrategy.execute(trucks, order, scheduler.getTime());
		
		if(listener != null)
			listener.onOrderProcessed(order);
		
		return result;
	}

	@Override
	public void onProcessProcessed(Process process) {
		if(listener != null) {
			if(process instanceof Truck) {
				listener.onTruckProcessed((Truck)process);
			}
		}
	}

	@Override
	public void onProcessUpdated(Process process) {
		if(listener != null) {
			if(process instanceof Truck) {
				listener.onTruckStateUpdated((Truck)process);
			}
		}
	}

	@Override
	public void onDecisionNewTruck(Order order, TruckPath path) {
		// vytvoreni noveho nakladaku
		Truck truck = new Truck(this, path);
		truck.setId(totalTruckCount);
		// pridani nakladaku do planovace
		addTruck(truck);
		
		order.setTruck(truck);
		
		// vyvolani udalosti pridani noveho nakladaku
		if(listener != null)
			listener.onTruckAdded(truck);
		
		// vyvolani udalosti prejeti objednavky
		if(listener != null)
			listener.onOrderReceived(order);
	}

	@Override
	public void onDecisionUseTruck(Order order, Truck truck,
			Vector<TruckPath> paths) {
		// predani nove naplanovane cesty vozidlu
		truck.setPaths(paths, getScheduler().getTime());
		
		order.setTruck(truck);
		
		// odstraneni vozidla z planovace
		scheduler.remove(truck);
		// znovuvlozeni vozidla do planovace
		scheduler.add(truck);
						
		// vyvolani udalosti preplanovani cesty vozidla
		if(listener != null) {
			listener.onTruckPathReplanned(truck);
		}
		
		// vyvolani udalosti prijeti objednavky
		if(listener != null)
			listener.onOrderReceived(order);
	}
	
	@Override
	public void onDecisionRefuseOrder(Order order) {
		Logger.addDebug("Objednavka'" + order + "' byla odmitnuta.", true);
		listener.onOrderRefused(order);
	}
	
}